//APB MASTER MODEL 
//DHEERAJ KUMAR BISWAS

//`timescale 1us/10ns
//`include "slaveSelection.v"
//========================DESIGN=========================================================
module apb(	PCLK,
           	PRESETn,
			PADDR,
           	PPROT,
           	PSELx,
           	PENABLE,
           	PSTRB,
	        PWRITE,
	        PWDATA,
	        PREADY,
	        PRDATA,
	        PSLVERR,
           	fifoWriteEnable,
			fifoReadEnable,
	        fifoFull,
	        fifoEmpty,
	        CMD);

  //parameters
  parameter DATA_WIDTH 			= 32;
  parameter ADDR_WIDTH 			= 10;
  parameter STRB_WIDTH			= 4;
  parameter WR1_RD0	 			= 1;
  parameter SLV_SEL 	 		= 2;
  						//parameter DEPTH 	 = 10;
  parameter CMD_LENGTH = DATA_WIDTH + ADDR_WIDTH + WR1_RD0 + SLV_SEL;

  //DECLARING THE PORTS
  //OUTPUT 
  output reg					PENABLE;
  output reg [STRB_WIDTH-1:0]	PSTRB;
  output reg 					PWRITE;
  output reg [DATA_WIDTH-1:0] 	PWDATA;
  output reg [ADDR_WIDTH-1:0]	PADDR;
  input 						PPROT;
  output reg [SLV_SEL-1:0]		PSELx;
  output reg 					fifoWriteEnable;
  output reg					fifoReadEnable;

  //INPUT
  input 						PCLK; 
  input 						PRESETn;
  input 						PREADY;
  input 						PSLVERR;
  input 	 [DATA_WIDTH-1:0] 	PRDATA;
  input 						fifoFull;
  input 						fifoEmpty;
  input 	 [CMD_LENGTH-1:0]	CMD;

  //DECLARING STATES and Internal parameters
  parameter  idle 				= 3'b001;
  parameter  setup 				= 3'b010;
  parameter  access 			= 3'b100;
  reg 		 [2:0]				pstate;
  reg  		 [2:0]				nstate;
  reg 		 [1:0]				count;
  reg		 [DATA_WIDTH-1:0]	register;

  always @(posedge PCLK or negedge PRESETn) begin
    if(!PRESETn) begin
	  	pstate <= idle;
      //    nstate <= idle;
    end
    /*else begin
      pstate <= nstate;
    end 
  end
  always @(pstate or negedge PRESETn or PRDATA or PSLVERR or PREADY or fifoEmpty) begin*/
    if(!PRESETn) begin
  		PENABLE <= 1'b0;
      PSELx 	<= 2'bzz;
      PWRITE 	<= 1'b0;
	  	count 	<= 2'b0;
    end
  	else if(PRESETn) begin
  		case(pstate)
        idle:begin 
            //PSELx <= slaveCount;
            //PWRITE 	<= CMD[2];
          if(!fifoEmpty) begin
            //fifoReadEnable <= 1'b1;
            control_setup;
            pstate <= setup;
          end
          else begin
            PENABLE <= 1'b0;
            PWRITE 	<= 1'b0;
            PSELx   <= 2'bzz;		//slaveCount;
            pstate <= idle;
          end
        end
    		setup:begin       
          	//fifoReadEnable <= 1'b1;
            control_setup;
    	      pstate 	<= access;
    	      count 	<= 2'b0;
          	PENABLE <= 1'b1; 
          	//fifoReadEnable <= 1'b0;
          	//register <= data;

            //assert the fifo fetch signal
        end
      	access:begin 
          	if(PREADY == 1'b1) begin
              if(CMD[2]) begin 
         	      PWDATA <= CMD[44:13];
              end
              else if(!CMD[2])begin
                register <= PRDATA;
              end
      		    //pstate <= idle; //setup;
              //PSELx <= 2'bzz;
              control_return;
            end 
            else begin                   //(PREADY == 1'b0 || 1'bz|| 1'bx) 
              if(count < 2'b10) begin
        	      pstate <= access;
        	      count = count + 1'b1;
              end
              else if(count >= 2'b10) begin         //this is not required as after the max wait time, pstate depends upon 
                                                    //the fifoEmpty that means it suspends the unacknowledged transfer
                //pstate <= idle;
                //PSELx <= 2'bzz;
                count = count + 1;
                control_return;
              end
            end
        end
      	default: begin pstate <= idle; end
  		endcase 
		end
	end
  
  task control_setup;
  begin
    if(CMD[2])
      PSTRB <= 4'b1111;     //add strb command in CMD
    else 
      PSTRB <= 4'b0000;
    PSELx    = CMD[1:0];
    PADDR   = CMD[12:3];
    PWDATA   = CMD[44:13];
    PWRITE = CMD[2];
  end
  endtask

  task control_return;
  begin
    if(fifoEmpty) begin  //if fifo is empty then go to idle state
      pstate <= idle;
      PWRITE <= 1'b0;
      PENABLE <= 1'b0;
      PSELx <= 2'bzz;
    end
    else begin        //else go to setup state
      pstate <= setup;
      //fifoReadEnable <= 1'b1;
      PWRITE  <= 1'b0;
      PENABLE <= 1'b0;
    end
  end
  endtask
endmodule
